package com.zzyl.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import com.github.xiaoymin.knife4j.core.util.CollectionUtils;
import com.zzyl.base.PageResponse;
import com.zzyl.constant.PendingTasksConstant;
import com.zzyl.dto.PendingTasksDto;
import com.zzyl.entity.PendingTasks;
import com.zzyl.entity.User;
import com.zzyl.mapper.HiActinstMapper;
import com.zzyl.service.ActFlowCommService;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.utils.StringUtils;
import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricActivityInstanceQuery;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ActFlowCommServiceImpl implements ActFlowCommService {
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;

    /**
     * 启动流程实例
     *
     * @param id                   入住表的主键id,业务key需要它去组装
     * @param processDefinitionKey checkIn
     * @param user                 当前用户
     * @param map                  流程变量
     * @param autoFinished         是否自动完成
     */
    @Override
    public void start(Long id, String processDefinitionKey, User user, Map<String, Object> map, boolean autoFinished) {

        String businessKey = processDefinitionKey + ":" + id;
        //启动流程实例
        runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, map);
        //        7,判断首节点的执行人(养老顾问)是否是当前用户
        //        8,如果是则自动完成该首节点的任务
        //查询任务
        List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey(processDefinitionKey)
                .taskAssignee(user.getId().toString())
                .list();
        //必须得是首节点才能继续向下执行
        list = list.stream().filter(t -> "0".equals(t.getFormKey())).collect(Collectors.toList());

        if (ObjectUtil.isNotEmpty(list) && autoFinished) {
            //完成任务
            for (Task task : list) {
                HashMap<String, Object> map1 = new HashMap<>();
                map1.put("ops", 1);//1代表通过
                map.put("processStatus", 1);//1代表申请中
                taskService.complete(task.getId(), map1);
            }

        }


    }


    /**
     * 获取下一个审核人id
     *
     * @param processDefinitionKey
     * @param businessKey
     * @return
     */
    @Override
    public Long getNextAssignee(String processDefinitionKey, String businessKey) {

        Task task = taskService.createTaskQuery().processDefinitionKey(processDefinitionKey)
                .processInstanceBusinessKey(businessKey).singleResult();
        if (ObjectUtil.isNotEmpty(task)) {
            return Long.valueOf(task.getAssignee());
        }
        return null;
    }

    @Autowired
    private HistoryService historyService;

    @Override
    public PageResponse<PendingTasks> myTaskList(PendingTasksDto dto) {
        //构建历史查询对象
        HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery();

        //查询已完成的任务还是未完成的任务
        if (ObjectUtil.isNotEmpty(dto.getIsHandle())) {
            if (dto.getIsHandle() == 1) {
                //如果前端点击的是已处理的按钮,那么isHandle的值就是1,所以我们需要查询的是完成的任务
                taskQuery.finished();
            } else {
                taskQuery.unfinished();
            }
        }

        //根据节点名称模糊查询
        if (ObjectUtil.isNotEmpty(dto.getAssigneeId())) {
            taskQuery.taskNameLike("%处理");
            //操作人
            taskQuery.taskAssignee(dto.getAssigneeId().toString());

        } else {
            taskQuery.taskNameLike("%申请");
            taskQuery.taskAssignee(dto.getApplicatId().toString());
        }


        //根据时间范围查询
        Date startTime = dto.getStartTime();
        Date endTime = dto.getEndTime();
        if (ObjectUtil.isNotEmpty(startTime) && ObjectUtil.isNotEmpty(endTime)) {
            taskQuery.taskCreatedAfter(startTime).taskCreatedBefore(endTime);
        }
        //申请人
        if (ObjectUtil.isNotEmpty(dto.getApplicat())) {
            taskQuery.processVariableValueEquals("assignee0Name", dto.getApplicat());
        }


        //单据类别（1：退住，2：请假，3：入住）
        if (ObjectUtil.isNotEmpty(dto.getType())) {
            taskQuery.processVariableValueEquals("processType", dto.getType());
        }
        //单据编号
        if (ObjectUtil.isNotEmpty(dto.getCode())) {
            taskQuery.processVariableValueEquals("processCode", dto.getCode());
        }
        //流程状态 （1：申请中，2:已完成，3:已关闭）
        if (ObjectUtil.isNotEmpty(dto.getStatus())) {
            taskQuery.processVariableValueEquals("processStatus", dto.getStatus());
        }

        //总条数
        long count = taskQuery.count();

        //分页查询
        int index = (dto.getPageNum() - 1) * dto.getPageSize();
        List<HistoricTaskInstance> list = taskQuery.includeProcessVariables().orderByHistoricTaskInstanceStartTime().desc()
                .listPage(index, dto.getPageSize());

        ArrayList<PendingTasks> pendingTasksList = new ArrayList<>();
        //遍历list,转换成PendingTasks
        for (HistoricTaskInstance task : list) {
            Map<String, Object> processVariables = task.getProcessVariables();

            PendingTasks pendingTasks = new PendingTasks();
            pendingTasks.setId(task.getId());//任务id
            pendingTasks.setCode(processVariables.get("processCode").toString());//入住编号
            pendingTasks.setType(Integer.parseInt(processVariables.get("processType").toString()));
            pendingTasks.setAssigneeId(Long.valueOf(task.getAssignee()));//操作人ID
            pendingTasks.setTitle(processVariables.get("processTitle").toString());//标题
            pendingTasks.setApplicat(processVariables.get("assignee0Name").toString());//申请人
            pendingTasks.setStatus(Integer.parseInt(processVariables.get("processStatus").toString()));//申请状态
            pendingTasks.setCheckInId(Long.valueOf(processVariables.get("processCheckInId").toString()));//入住id
            LocalDateTime localDateTime = LocalDateTimeUtil.parse(processVariables.get("applicationTime").toString());
            pendingTasks.setApplicationTime(localDateTime);

            //如果当前的审核状态status是已结束或者已完成,才需要返回完成时间
            if (pendingTasks.getStatus().equals(PendingTasksConstant.TASK_STATUS_CLOSED) || pendingTasks.getStatus().equals(PendingTasksConstant.TASK_STATUS_FINISHED)) {
                LocalDateTime finishTime = LocalDateTimeUtil.parse(processVariables.get("finishTime").toString());
                pendingTasks.setFinishTime(finishTime);
            }


            pendingTasksList.add(pendingTasks);
        }

        //计算总页数
        // (总条数 + 每页显示记录数 - 1)  / 每页显示记录数
        long pages = (count + dto.getPageSize() - 1) / dto.getPageSize();

        return PageResponse.of(pendingTasksList, dto.getPageNum(), dto.getPageSize(), pages, count);
    }

    @Override
    public Integer isCurrentUserAndStep(String taskId, Integer flowStatus, Integer dbFlowStatus) {
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
        String formKey = historicTaskInstance.getFormKey();

        /*
        前端传递的流程状态和数据库的流程状态一致,此时分两种情况:
        是否是当前登录人的任务,如果是返回1,如果不是返回0
         */
        if (flowStatus.equals(dbFlowStatus)) {
            if (formKey.equals(dbFlowStatus.toString())) {
                return 1;
            }
            return 0;
        }

        if (formKey.equals((dbFlowStatus - 1) + "")) {
            return 2;
        }

        return 1;
    }

    /**
     * 完成流程
     *
     * @param title  标题
     * @param taskId 任务ID
     * @param userId 当前用户ID
     * @param code   ops  1:同意  2:拒绝  3:驳回
     * @param status 审核状态  1:申请中  2:已完成  3:已结束
     */
    @Override
    public void completeProcess(String title, String taskId, String userId, Integer code, Integer status) {
        Task task = taskService.createTaskQuery()
                .taskId(taskId).singleResult();
        if (ObjectUtil.isEmpty(task)) {
            return;
        }

        //如果当前是驳回操作,则需要删除流程实例的任务记录
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).list();
        if (ObjectUtil.isNotEmpty(list)) {
            list.forEach(t -> {
                //如果状态是驳回,则删除第一个节点的任务记录,因为完成任务之后,会重新分配一个新任务
                if (code.equals(3) && "0".equals(t.getFormKey())) {
                    historyService.deleteHistoricTaskInstance(t.getId());
                }
                /*
                删除当前任务之后的任务记录,不然可能会存在多条数据,意思就是在驳回之后,就相当于又回到了养老顾问的入住申请那里,
                但是此时护理主管已经有一条数据了,就是之前的历史数据,所以需要删除历史数据
                 */

                if (t.getFormKey().equals((Integer.parseInt(task.getFormKey()) + 1) + "")) {
                    historyService.deleteHistoricTaskInstance(t.getId());
                }
            });
        }


        HashMap<String, Object> map = new HashMap<>();
        if (ObjectUtil.isNotEmpty(status)) {
            map.put("processStatus", status);
            //完成时间
            map.put("finishTime", LocalDateTime.now());
        }
        if (ObjectUtil.isNotEmpty(title)) {
            map.put("processTitle", title);
        }
        map.put("ops", code);//1表示同意
        taskService.complete(taskId, map);
    }

    /**
     * 撤回任务
     *
     * @param taskId
     * @param first  是否默认退回流程第一个节点，true 是,false默认是上一个节点，
     */
    @Override
    public void withdrawTask(String taskId, boolean first) {
        anyJump(taskId, first);
    }

    /**
     * 关闭流程 (删除流程实例)
     * @param taskId 任务ID
     * @param code 审核状态 1:申请中  2:已完成  3:已结束
     */
    @Override
    public void closeProcess(String taskId, Integer code) {
        //从任务中拿到流程实例id
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
        String processInstanceId = historicTaskInstance.getProcessInstanceId();

        //记录流程变量 (留痕)
        HashMap<String, Object> map = new HashMap<>();
        map.put("processStatus",code);
        map.put("finishTime",LocalDateTime.now());

        runtimeService.setVariables(processInstanceId,map);
        //删除流程实例
        runtimeService.deleteProcessInstance(processInstanceId,"申请人撤销");

    }

    @Override
    public void rollBackTask(String taskId, boolean b) {
        anyJump(taskId, b);
    }

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private HiActinstMapper hiActinstMapper;
    /**
     * 跳转任意节点
     *
     * @param taskId 当前操作节点
     * @param first  是否默认第一 是否驳回
     */
    public void anyJump(String taskId, boolean first) {
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
        //实例定义id：checkIn:1:0f97a26d-5697-11ee-bf3f-5405db5be13e
        String processDefinitionId = historicTaskInstance.getProcessDefinitionId();
        //实例id：16ea626d-5755-11ee-849a-5405db5be13e
        String processInstanceId = historicTaskInstance.getProcessInstanceId();
        // 对上一个节点和发起节点的支持:Activity_0pnd103
        String activityId = null;
        //找到需要驳回的节点中，比如：现在是：养老顾问-入住配置，那么要找的就是上一个节点：副院长-审批
        HistoricActivityInstance targetActivity = getRejectTargetActivity(null, processInstanceId, first);
        if (targetActivity != null) {
            activityId = targetActivity.getActivityId();
        }
        if (StrUtil.isEmpty(activityId)) {
            return;
        }
        try {
            //获取流程中的bpmn文件
            BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
            //流程实例
            Process process = bpmnModel.getMainProcess();
            // 解析调整的目标节点
            //找到目标节点  -->  副院长-审批
            FlowNode targetFlowNode = (FlowNode) process.getFlowElement(activityId);
            //找到当前节点的所有连线
            List<SequenceFlow> incomingFlows = targetFlowNode.getIncomingFlows();

            List<SequenceFlow> targetSequenceFlow = new ArrayList<>();
            //遍历所有连线
            for (SequenceFlow incomingFlow : incomingFlows) {
                //连线的入节点
                FlowNode source = (FlowNode) incomingFlow.getSourceFlowElement();
                List<SequenceFlow> sequenceFlows;
                if (source instanceof ParallelGateway) {// 如果是并行网关同级节点，则跳转到所有节点
                    sequenceFlows = source.getOutgoingFlows();
                } else {
                    sequenceFlows = source.getOutgoingFlows();// 否则直接跳转到对应节点，包括为执行过的节点
                }
                targetSequenceFlow.addAll(sequenceFlows);
            }
            //获取当前任务中的所有待执行的任务
            List<Task> list = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
            for (Task t : list) {
                //把一个任务动态转向目标节点
                //参数1：目标节点   参数2：当前任务   参数3：当前任务id    参数4:目标节点所有连线   参数5：默认flase，找上一个节点
                trunToTarget(process, t, first ? taskId : list.get(0).getId(), targetSequenceFlow, first);
            }
            if (!first) { // 撤回 删除最后的节点
                historyService.deleteHistoricTaskInstance(taskId);
                hiActinstMapper.deleteHiActivityInstByTaskId(taskId);
            } else {
                // 撤回 删除第一个
                List<HistoricTaskInstance> list1 = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).finished().orderByTaskCreateTime().asc().list();
                if (CollUtil.isNotEmpty(list1)) {
                    HistoricTaskInstance firstTask = list1.get(0);
                    historyService.deleteHistoricTaskInstance(firstTask.getId());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 把一个任务动态转向目标节点
     * @param process   目标节点
     * @param task  当前任务
     * @param taskId             当前任务id
     * @param targetSequenceFlow  目标节点所有连线
     * @param first  默认flase，找上一个节点
     */
    private void trunToTarget(Process process, TaskInfo task, String
            taskId, List<SequenceFlow> targetSequenceFlow, boolean first) {

        //当前节点:入住选配-处理
        FlowNode curFlowNode = (FlowNode) process.getFlowElement(task.getTaskDefinitionKey());
        if (curFlowNode == null) {
            //遍历节点中的所有子模块
            for (FlowElement flowElement : process.getFlowElements()) {
                if (flowElement instanceof SubProcess) {
                    SubProcess subProcess = (SubProcess) flowElement;
                    FlowElement fe = subProcess.getFlowElement(task.getTaskDefinitionKey());
                    if (fe != null) {
                        curFlowNode = (FlowNode) fe;
                        break;
                    }
                }
            }
        }
        //备份原始连线
        List<SequenceFlow> tempOutgoingSequenceFlows = new ArrayList<>(curFlowNode.getOutgoingFlows());
        //最新任务id与要删除的id一致
        if (taskId.equals(task.getId())) {
            //当前节点设置流出的连线
            curFlowNode.setOutgoingFlows(targetSequenceFlow);
            //完成当前任务
            taskService.complete(task.getId());
            if (!first) {
                //删除任务实例
                historyService.deleteHistoricTaskInstance(task.getId());
                //删除历史任务
                hiActinstMapper.deleteHiActivityInstByTaskId(task.getId());
            }
        }
        //恢复之前的连线
        curFlowNode.setOutgoingFlows(tempOutgoingSequenceFlows);
    }

    /**
     * 获取历史撤回或回退目标节点,支持上一节点，第一个节点
     *
     * @param taskId            要回退的taskId
     * @param processInstanceId
     * @return
     */
    private HistoricActivityInstance getRejectTargetActivity(String taskId, String processInstanceId, boolean first) {

        HistoricActivityInstance targetActivity = null;
        HistoricActivityInstanceQuery query = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).activityType("userTask");

        // 取得所有历史任务按时间降序排序
        List<HistoricActivityInstance> historicActivityInstances = null;
        if (first) {// 退到第一个节点
            historicActivityInstances = query.orderByHistoricActivityInstanceStartTime().asc().list();
            return historicActivityInstances.get(0);
        } else { // 找到最近一个节点
            historicActivityInstances = query.orderByHistoricActivityInstanceStartTime().desc().list();
        }

        if (CollectionUtils.isEmpty(historicActivityInstances) || historicActivityInstances.size() < 2) {
            return null;
        }
        if (!StringUtils.isBlank(taskId)) {
            return targetActivity;
        }
        // 不传活动id的情况直接找第一个任务
        // 最后一条是当前正在进行的任务 需要找到最近的但是名称和当前任务不一样的任务去撤回
        HistoricActivityInstance current = historicActivityInstances.get(0);
        for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
            if (!current.getActivityId().equals(historicActivityInstance.getActivityId())) {
                if (historicActivityInstance.getActivityType().equals("userTask")) {
                    targetActivity = historicActivityInstance;
                    break;
                }
            }
        }
        return targetActivity;
    }
}
